/*
 * 
 * 
 * 
 */
package com.hboxs.asl.service.impl;

import com.hboxs.asl.FileInfo;
import com.hboxs.asl.FileInfo.FileType;
import com.hboxs.asl.FileInfo.OrderType;
import com.hboxs.asl.Setting;
import com.hboxs.asl.plugin.StoragePlugin;
import com.hboxs.asl.service.FileService;
import com.hboxs.asl.service.PluginService;
import com.hboxs.asl.util.FreemarkerUtils;
import com.hboxs.asl.util.MerchantUtils;
import com.hboxs.asl.util.SettingUtils;

import freemarker.template.TemplateException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletContext;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * Service - 文件
 */
@Service("fileServiceImpl")
public class FileServiceImpl implements FileService, ServletContextAware {

    /**
     * servletContext
     */
    private ServletContext servletContext;

    @Resource(name = "taskExecutor")
    private TaskExecutor taskExecutor;
    @Resource(name = "pluginServiceImpl")
    private PluginService pluginService;

    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    /**
     * 添加上传任务
     *
     * @param storagePlugin 存储插件
     * @param path          上传路径
     * @param tempFile      临时文件
     * @param contentType   文件类型
     */
    private void addTask(final StoragePlugin storagePlugin, final String path, final File tempFile, final String contentType) {
        taskExecutor.execute(new Runnable() {
            public void run() {
                try {
                    storagePlugin.upload(path, tempFile, contentType);
                } finally {
                    FileUtils.deleteQuietly(tempFile);
                }
            }
        });
    }

    public boolean isValid(FileType fileType, MultipartFile multipartFile) {
        if (multipartFile == null) {
            return false;
        }
        Setting setting = SettingUtils.get();
        if (setting.getUploadMaxSize() != null && setting.getUploadMaxSize() != 0 && multipartFile.getSize() > setting.getUploadMaxSize() * 1024L * 1024L) {
            return false;
        }
        String[] uploadExtensions;
        if (fileType == FileType.flash) {
            uploadExtensions = setting.getUploadFlashExtensions();
        } else if (fileType == FileType.media) {
            uploadExtensions = setting.getUploadMediaExtensions();
        } else if (fileType == FileType.file) {
            uploadExtensions = setting.getUploadFileExtensions();
        } else {
            uploadExtensions = setting.getUploadImageExtensions();
        }
        if (ArrayUtils.isNotEmpty(uploadExtensions)) {
            return FilenameUtils.isExtension(multipartFile.getOriginalFilename(), uploadExtensions);
        }
        return false;
    }

    public String upload(FileType fileType, MultipartFile multipartFile, boolean async, String imageTypePath) {
        if (multipartFile == null) {
            return null;
        }
        Setting setting = SettingUtils.get();
        String uploadPath;
        if (fileType == FileType.flash) {
            uploadPath = setting.getFlashUploadPath();
        } else if (fileType == FileType.media) {
            uploadPath = setting.getMediaUploadPath();
        } else if (fileType == FileType.file) {
            uploadPath = setting.getFileUploadPath();
        } else if (fileType == FileType.avator) {
            uploadPath = setting.getAvatorUploadPath();
        } else if (fileType == FileType.avatorTmp) {
            uploadPath = setting.getAvatorTmpUploadPath();
        } else {
            uploadPath = setting.getImageUploadPath();
        }
        try {
            Map<String, Object> model = new HashMap<String, Object>();
            model.put("uuid", UUID.randomUUID().toString());
            if (StringUtils.isEmpty(imageTypePath)) {
                imageTypePath = "other";
            }
            model.put("imageType", imageTypePath);

            String path = FreemarkerUtils.process(uploadPath, model);
            String originalName = FilenameUtils.getBaseName(multipartFile.getOriginalFilename()).replaceAll(" ","_");
            String destPath = path +  originalName + "_" +UUID.randomUUID() + "." + FilenameUtils.getExtension(multipartFile.getOriginalFilename());
            for (StoragePlugin storagePlugin : pluginService.getStoragePlugins(true)) {
                File tempFile = new File(System.getProperty("java.io.tmpdir") + "/upload_" + UUID.randomUUID() + ".tmp");
                if (!tempFile.getParentFile().exists()) {
                    tempFile.getParentFile().mkdirs();
                }
                multipartFile.transferTo(tempFile);
                if (async) {
                    addTask(storagePlugin, destPath, tempFile, multipartFile.getContentType());
                } else {
                    try {
                        storagePlugin.upload(destPath, tempFile, multipartFile.getContentType());
                    } finally {
                        FileUtils.deleteQuietly(tempFile);
                    }
                }
                return storagePlugin.getUrl(destPath);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String uploadMedia(FileType fileType, MultipartFile multipartFile) {
        if (multipartFile == null) {
            return null;
        }
        Setting setting = SettingUtils.get();
        String uploadPath = setting.getMediaUploadPath();
        try {
            Map<String, Object> model = new HashMap<String, Object>();
            model.put("uuid", UUID.randomUUID().toString());

            String path = FreemarkerUtils.process(uploadPath, model);
            String originalName = FilenameUtils.getBaseName(multipartFile.getOriginalFilename()).replaceAll(" ","_");
            String destPath = path +  originalName + "_" +UUID.randomUUID() + "." + FilenameUtils.getExtension(multipartFile.getOriginalFilename());
            for (StoragePlugin storagePlugin : pluginService.getStoragePlugins(true)) {
                File tempFile = new File(System.getProperty("java.io.tmpdir") + "/upload_" + UUID.randomUUID() + ".tmp");
                if (!tempFile.getParentFile().exists()) {
                    tempFile.getParentFile().mkdirs();
                }
                multipartFile.transferTo(tempFile);
                    try {
                        storagePlugin.upload(destPath, tempFile, multipartFile.getContentType());
                    } finally {
                        FileUtils.deleteQuietly(tempFile);
                    }
                return storagePlugin.getUrl(destPath);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void change2Mp3() {
        
    }

    public String merchantUpload(FileType fileType, MultipartFile multipartFile, boolean async, String imageTypePath) {
        if (multipartFile == null) {
            return null;
        }
        Setting setting = SettingUtils.get();
        String uploadPath;
        if (fileType == FileType.flash) {
            uploadPath = setting.getFlashUploadPath();
        } else if (fileType == FileType.media) {
            uploadPath = setting.getMediaUploadPath();
        } else if (fileType == FileType.file) {
            uploadPath = setting.getFileUploadPath();
        } else if (fileType == FileType.avator) {
            uploadPath = setting.getAvatorUploadPath();
        } else if (fileType == FileType.avatorTmp) {
            uploadPath = setting.getAvatorTmpUploadPath();
        } else {
            uploadPath = setting.getMerchantImageUploadPath();
        }
        try {
            Map<String, Object> model = new HashMap<String, Object>();
            model.put("uuid", UUID.randomUUID().toString());
            model.put("merchantId", MerchantUtils.getCurrentMerchant().getId());
            if (StringUtils.isEmpty(imageTypePath)) {
                imageTypePath = "other";
            }
            model.put("imageType", imageTypePath);

            String path = FreemarkerUtils.process(uploadPath, model);
            String originalName = FilenameUtils.getBaseName(multipartFile.getOriginalFilename()).replaceAll(" ","_");
            String destPath = path +  originalName + "_" +UUID.randomUUID() + "." + FilenameUtils.getExtension(multipartFile.getOriginalFilename());
            for (StoragePlugin storagePlugin : pluginService.getStoragePlugins(true)) {
                File tempFile = new File(System.getProperty("java.io.tmpdir") + "/upload_" + UUID.randomUUID() + ".tmp");
                if (!tempFile.getParentFile().exists()) {
                    tempFile.getParentFile().mkdirs();
                }
                multipartFile.transferTo(tempFile);
                if (async) {
                    addTask(storagePlugin, destPath, tempFile, multipartFile.getContentType());
                } else {
                    try {
                        storagePlugin.upload(destPath, tempFile, multipartFile.getContentType());
                    } finally {
                        FileUtils.deleteQuietly(tempFile);
                    }
                }
                return storagePlugin.getUrl(destPath);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public String upload(FileType fileType, MultipartFile multipartFile, String imageTypePath) {
        return upload(fileType, multipartFile, false, imageTypePath);
    }

    public String uploadLocal(FileType fileType, MultipartFile multipartFile) {
        if (multipartFile == null) {
            return null;
        }
        Setting setting = SettingUtils.get();
        String uploadPath;
        if (fileType == FileType.flash) {
            uploadPath = setting.getFlashUploadPath();
        } else if (fileType == FileType.media) {
            uploadPath = setting.getMediaUploadPath();
        } else if (fileType == FileType.file) {
            uploadPath = setting.getFileUploadPath();
        } else {
            uploadPath = setting.getImageUploadPath();
        }
        try {
            Map<String, Object> model = new HashMap<String, Object>();
            model.put("uuid", UUID.randomUUID().toString());

            String path = FreemarkerUtils.process(uploadPath, model);
            String destPath = path + UUID.randomUUID() + "." + FilenameUtils.getExtension(multipartFile.getOriginalFilename());
            File destFile = new File(setting.getRootPath() + destPath);
            if (!destFile.getParentFile().exists()) {
                destFile.getParentFile().mkdirs();
            }
            multipartFile.transferTo(destFile);
            return destPath;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<FileInfo> browser(String path, FileType fileType, OrderType orderType) {
        if (path != null) {
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (!path.endsWith("/")) {
                path += "/";
            }
        } else {
            path = "/";
        }
        Setting setting = SettingUtils.get();
        String uploadPath;
        if (fileType == FileType.flash) {
            uploadPath = setting.getFlashUploadPath();
        } else if (fileType == FileType.media) {
            uploadPath = setting.getMediaUploadPath();
        } else if (fileType == FileType.file) {
            uploadPath = setting.getFileUploadPath();
        } else {
            uploadPath = setting.getImageUploadPath();
        }

        String browsePath = StringUtils.substringBefore(uploadPath, "${");
        browsePath = StringUtils.substringBeforeLast(browsePath, "/") + path;

        List<FileInfo> fileInfos = new ArrayList<FileInfo>();
        if (browsePath.indexOf("..") >= 0) {
            return fileInfos;
        }
        for (StoragePlugin storagePlugin : pluginService.getStoragePlugins(true)) {
            fileInfos = storagePlugin.browser(browsePath);
            break;
        }
        if (orderType == OrderType.size) {
            Collections.sort(fileInfos, new SizeComparator());
        } else if (orderType == OrderType.type) {
            Collections.sort(fileInfos, new TypeComparator());
        } else {
            Collections.sort(fileInfos, new NameComparator());
        }
        return fileInfos;
    }

    public List<FileInfo> merchantBrowser(String path, FileType fileType, OrderType orderType) {
        if (path != null) {
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (!path.endsWith("/")) {
                path += "/";
            }
        } else {
            path = "/";
        }
        Setting setting = SettingUtils.get();
        String uploadPath;
        if (fileType == FileType.flash) {
            uploadPath = setting.getFlashUploadPath();
        } else if (fileType == FileType.media) {
            uploadPath = setting.getMediaUploadPath();
        } else if (fileType == FileType.file) {
            uploadPath = setting.getFileUploadPath();
        } else {
            uploadPath = setting.getMerchantImageUploadPath();
        }
        String browsePath = StringUtils.substringBefore(uploadPath, "${imageType}");
        browsePath = StringUtils.substringBeforeLast(browsePath, "/") + path;

        Map<String, Object> model = new HashMap<String, Object>();
        model.put("merchantId", MerchantUtils.getCurrentMerchant().getId());
        try {
            browsePath = FreemarkerUtils.process(browsePath, model);
        } catch (Exception e) {
            e.printStackTrace();
        }

        List<FileInfo> fileInfos = new ArrayList<FileInfo>();
        if (browsePath.indexOf("..") >= 0) {
            return fileInfos;
        }
        for (StoragePlugin storagePlugin : pluginService.getStoragePlugins(true)) {
            fileInfos = storagePlugin.browser(browsePath);
            break;
        }
        if (orderType == OrderType.size) {
            Collections.sort(fileInfos, new SizeComparator());
        } else if (orderType == OrderType.type) {
            Collections.sort(fileInfos, new TypeComparator());
        } else {
            Collections.sort(fileInfos, new NameComparator());
        }
        return fileInfos;
    }


    private class NameComparator implements Comparator<FileInfo> {
        public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
            return new CompareToBuilder().append(!fileInfos1.getIsDirectory(), !fileInfos2.getIsDirectory()).append(fileInfos1.getName(), fileInfos2.getName()).toComparison();
        }
    }

    private class SizeComparator implements Comparator<FileInfo> {
        public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
            return new CompareToBuilder().append(!fileInfos1.getIsDirectory(), !fileInfos2.getIsDirectory()).append(fileInfos1.getSize(), fileInfos2.getSize()).toComparison();
        }
    }

    private class TypeComparator implements Comparator<FileInfo> {
        public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
            return new CompareToBuilder().append(!fileInfos1.getIsDirectory(), !fileInfos2.getIsDirectory())
                    .append(FilenameUtils.getExtension(fileInfos1.getName()), FilenameUtils.getExtension(fileInfos2.getName())).toComparison();
        }
    }

    @Override
    public boolean del(FileType fileType, String fileName) {
        String uploadPath;
        Setting setting = SettingUtils.get();
        uploadPath = setting.getUploadPath();
        Map<String, Object> model = new HashMap<String, Object>();
        String path;
        try {
            path = FreemarkerUtils.process(uploadPath, model);
            String destPath = path + fileType.toString() + fileName;
            File file = new File(setting.getRootPath() + destPath);
            if (file.exists()) {
                file.delete();
                return true;
            } else {
                System.err.println("File Not Found!" + setting.getRootPath() + destPath);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
        return false;
    }

}